package it.unibo.refolding.pico;

import it.unibo.refolding.pico.PicoDataAnalyser.ContactPointAndSlope;
import it.unibo.refolding.pico.stripchart.AnalysisOptions;
import it.unibo.refolding.pico.stripchart.JpHelper;

import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.Parser;
import org.apache.commons.lang3.StringUtils;

import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;

public class RetractionTraceExporter {
  private static final Logger logger = Logger.getLogger(RetractionTraceExporter.class.getSimpleName());

  public static void main(String[] args) throws IOException, ParseException {
    System.setProperty("java.awt.headless", "true");

    Options options = new Options();

    Option contactPointAndSlopeDirOption = ContactPointAndSlopeHelper.createContactPointAndSlopeDirOption();
    options.addOption(contactPointAndSlopeDirOption);

    Option wildcardPatternOption = PicoUtil.createWildcardPatternOption();
    options.addOption(wildcardPatternOption);

    Option dataDirOption = PicoUtil.createDataDirOption();
    options.addOption(dataDirOption);

    Option inputPathListOption = PicoUtil.createInputPathListOption();
    options.addOption(inputPathListOption);

    Option fileFormatOption = AnalysisOptions.createFileFormatOption();
    options.addOption(fileFormatOption);

    Option junctionPointsDirOption = JpHelper.createJunctionPointsOption();
    options.addOption(junctionPointsDirOption);

    Option fixedDeflectionSensitivityNmPerVOption = PicoUtil.createFixedDeflectionSensitivityNmPerVOption();
    options.addOption(fixedDeflectionSensitivityNmPerVOption);

    Option fixedSpringConstantNPerMOption = PicoUtil.createFixedSpringConstantNPerMOption();
    options.addOption(fixedSpringConstantNPerMOption);

    Option tracesPathOption = new Option(
      "tracesPath", true, "name of retraction data playlist file to be created");
    tracesPathOption.setRequired(true);
    tracesPathOption.setType(String.class);
    options.addOption(tracesPathOption);

    Parser parser = new GnuParser();
    CommandLine line = parser.parse(options, args);
    String dataDir = line.getOptionValue(dataDirOption.getOpt());
    String inputPathList = line.getOptionValue(inputPathListOption.getOpt());
    Preconditions.checkArgument(
      StringUtils.isEmpty(dataDir) ^ StringUtils.isEmpty(inputPathList),
      "Must specify exactly one of dataDir and inputPathList.");
    String wildcardPattern = line.getOptionValue(wildcardPatternOption.getOpt());
    FileFormat fileFormat = FileFormat.valueOf(line.getOptionValue(
      fileFormatOption.getOpt(), FileFormat.NANOSCOPE.name()).toUpperCase(Locale.ROOT));
    String contactPointAndSlopeDir = line.getOptionValue(contactPointAndSlopeDirOption.getOpt());
    Path contactPointAndSlopeDirectory = Paths.get(contactPointAndSlopeDir);
    String junctionPointsDir = line.getOptionValue(junctionPointsDirOption.getOpt());
    Path junctionPointsDirectory = StringUtils.isEmpty(junctionPointsDir) ? null : Paths.get(
        junctionPointsDir);
    String fixedSpringConstantNPerMString = line.getOptionValue(fixedSpringConstantNPerMOption.getOpt());
    final double fixedSpringConstantNPerM = StringUtils.isEmpty(fixedSpringConstantNPerMString) ? Double.NaN
      : Double.parseDouble(fixedSpringConstantNPerMString);
    String fixedDeflectionSensitivityNmPerVString = line.getOptionValue(fixedDeflectionSensitivityNmPerVOption.getOpt());
    final double fixedDeflectionSensitivityMPerV = StringUtils.isEmpty(fixedDeflectionSensitivityNmPerVString) ? Double.NaN
      : Double.parseDouble(fixedDeflectionSensitivityNmPerVString) / 1e9;
    String tracesPath = line.getOptionValue(tracesPathOption.getOpt());
    List<Path> files = PicoUtil.computeFileList(dataDir, inputPathList, wildcardPattern);
    List<RetractionTrace> traces = new ArrayList<>();
    for (Path file : files) {
      Parsed parsed = PicoUtil.fix(
        PicoUtil.parse(file, fileFormat, null, null, null, junctionPointsDirectory),
        fixedDeflectionSensitivityMPerV, fixedSpringConstantNPerM);
      Path contactPointAndSlopeFile = ContactPointAndSlopeHelper.contactPointAndSlopeFile(
        contactPointAndSlopeDirectory, file.getFileName().toString());
      ContactPointAndSlope cps = ContactPointAndSlopeHelper.extractContactPointAndSlope(contactPointAndSlopeFile);
      final RetractionTrace trace;
      if (parsed.isEmpty()) {
        trace = new RetractionTrace(
          file.toString(), Double.NaN, Double.NaN, null, ImmutableList.<DirectionData>of());
      } else {
        PicoData last = parsed.last();
        List<DirectionData> dds = new ArrayList<>();
        for (PicoData pd : parsed.picoDataList) {
          PlainPicoData plainPicoData = (PlainPicoData) pd;
          Preconditions.checkState(last.deflectionLsbToMetresFactor() == plainPicoData.deflectionLsbToMetresFactor());
          Preconditions.checkState(last.zLsbToMetresFactor == plainPicoData.zLsbToMetresFactor);
          dds.add(plainPicoData.sharedRetraction());
        }
        trace = new RetractionTrace(
          file.toString(), last.deflectionLsbToMetresFactor(), last.zLsbToMetresFactor, cps, dds);
      }
      traces.add(trace);
    }
    try (Writer retractionFw = Files.newBufferedWriter(Paths.get(tracesPath), Charsets.ISO_8859_1)) {
      for (RetractionTrace trace : traces) {
        retractionFw.append(trace.repr());
        retractionFw.append('\n');
      }
    }
    logger.log(Level.INFO, "Wrote {0} traces in: {1}", new Object[] {traces.size(), tracesPath});
  }
}
